Skip to content

XCM NFT types that use Granular NFT traits #4300

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 60 commits into
base: master
Choose a base branch
from

Conversation

mrshiposha
Copy link
Contributor

@mrshiposha mrshiposha commented Apr 25, 2024

Overview

This PR provides new XCM types and tools for building NFT Asset Transactors.
The new types use general and granular NFT traits from #5620.

The new XCM adapters and utility types to work with NFTs can be considered the main deliverable of the XCM NFT proposal. The new types use a more general approach, making integration into any chain with various NFT implementations easier.

For instance, different implementations could use:

  • different ID assignment approaches
    • predefined NFT IDs - pallet-uniques, pallet-nfts
    • derived NFT IDs (NFT IDs are automatically derived from collection IDs) - Unique Network, ORML/Acala, Aventus
  • classless (collection-less) tokens - CoreTime NFTs, Aventus NFT Manager NFTs
  • in-class (in-collection) tokens - Unique Network, pallet-uniques, pallet-nfts, ORML/Acala
  • different approaches to storing associated data on-chain:
    • data is stored entirely separately from tokens - pallet-uniques
    • data is stored partially or entirely within tokens (i.e., burning a token means destroying it with its data) - pallet-nfts (partially), Unique Network, ORML/Acala

With new types, these differences can be abstracted away.

Moreover, the new types provide greater flexibility for supporting derivative NFTs, allowing several possible approaches depending on the given chain's team's goals or restrictions (see the pallet-derivatives crate docs and mock docs).

Also, this is the PR I mentioned in the #4073 issue, as it can be viewed as the solution. In particular, the new adapter (UniqueInstancesAdapter) requires the Update operation with the ChangeOwnerFrom strategy. This brings the attention of both a developer and a reviewer to the ChangeOwnerFrom strategy (meaning that the transfer checks if the asset can be transferred from a given account to another account), both at trait bound and at the call site, without sacrificing the flexibility of the NFT traits.

New types for xcm-builder and xcm-executor

This PR introduces several XCM types.

The UniqueInstancesAdapter is a new TransactAsset adapter that supersedes both NonFungibleAdapter and NonFungiblesAdapter (for reserve-based transfers only, as teleports can't be implemented appropriately without transferring the NFT metadata alongside it; no standard solution exists for that yet. Hopefully, the Felloweship RFC 125 (PR, text) will help with that).

Thanks to the new Matcher types, the new adapter can be used instead of both NonFungibleAdapter and NonFungiblesAdapter:

  • MatchesInstance (a trait)
  • MatchInClassInstances
  • MatchClasslessInstances

The UniqueInstancesAdapter works with existing tokens only. To create new tokens (derivative ones), there is the UniqueInstancesDepositAdapter.

See the pallet-derivatives mock and tests for the usage example.

Superseding the old adapters for pallet-uniques

Here is how the new UniqueInstancesAdapter in Westend Asset Hub replaces the NonFungiblesAdapter:

/// Means for transacting unique assets.
pub type UniquesTransactor = UniqueInstancesAdapter<
	AccountId,
	LocationToAccountId,
	MatchInClassInstances<UniquesConvertedConcreteId>,
	pallet_uniques::asset_ops::Item<Uniques>,
>;

MatchInClassInstances allows us to reuse the already existing UniquesConvertedConcreteId matcher.
The pallet_uniques::asset_ops::Item<Uniques> already implements the needed traits.

So, migrating from the old adapter to the new one regarding runtime config changes is easy.

NOTE: pallet_uniques::asset_ops::Item grants access to the asset operations of NFT items of a given pallet-uniques instance, whereas pallet_uniques::asset_ops::Collection grants access to the collection operations.

Declarative modification of an NFT engine

If an NFT-hosting pallet only implements a transfer operation but not the Stash and Restore, one could declaratively add them using the UniqueInstancesWithStashAccount adapter.

So, you can use it with the UniqueInstancesAdapter as follows:

parameter_types! {
    	pub StashAccountId: AccountId = /* Some Stash Account ID */;
}

type Transactor = UniqueInstancesAdapter<
	AccountId,
	LocationToAccountId,
	Matcher,
	UniqueInstancesWithStashAccount<StashAccountId, NftEngine>,
>;

Supporting derivative NFTs (reserve-based model)

There are several possible scenarios of supporting derivative NFTs (and their collections, if applicable).

A separate NFT-hosting pallet instance could be configured to use XCM AssetId as collection ID and AssetInstance token ID. In that case, the asset transaction doesn't need any special treatment.

However, registering NFT collections might require a special API.
Also, if the NFT-hosting pallet can't be configured to use XCM ID types, we would need to store a mapping between the XCM ID of the original token and the derivative ID.

See the pallet-derivatives crate docs for details.
The example of its usage can be found in its mock and tests.

TODO

No benchmarks were run for pallet-derivatives, so there is no weights.rs for it yet.

@mrshiposha
Copy link
Contributor Author

CC @franciscoaguirre

@mrshiposha
Copy link
Contributor Author

mrshiposha commented Jul 2, 2024

@franciscoaguirre @xlc This PR adds new granular NFT traits to frame-support and provides new XCM types, giving us the instruments to implement XCM NFT in any chain regardless of differences in NFT solutions used in the ecosystem (different pallets, which represent NFTs differently or incompatible with each other, or smart contract-based solutions).

This PR could undoubtedly be divided into at least two. However, I decided to provide all the pieces at once so we can discuss the complete picture. I can divide the PR if needed.

@mrshiposha mrshiposha changed the title Feature/asset ops traits Granular NFT traits and new XCM NFT types Jul 2, 2024
@mrshiposha mrshiposha marked this pull request as ready for review July 2, 2024 17:57
@mrshiposha mrshiposha requested review from a team as code owners July 2, 2024 17:57
@mrshiposha
Copy link
Contributor Author

@acatangiu @franciscoaguirre Hello!
I updated the PR using the types from #5620, refactored it, added doc comments, and updated the PR's description.

Could you guys please take a look? 🙏

@raymondkfcheung raymondkfcheung added the T6-XCM This PR/Issue is related to XCM. label May 29, 2025
@raymondkfcheung raymondkfcheung moved this from Todo to In Progress in Bridges + XCM May 29, 2025
@raymondkfcheung raymondkfcheung moved this from In Progress to In-Review in Bridges + XCM May 29, 2025
@mrshiposha mrshiposha changed the title Granular NFT traits and new XCM NFT types XCM NFT types that use Granular NFT traits May 30, 2025
Copy link
Contributor

@jsidorenko jsidorenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice and really well thought!

@gztensor
Copy link

gztensor commented Jun 11, 2025

I see quite a few very useful changes here:

  • Traits for flexible NFT ID handling
  • Support for creation of derivative NFTs -> a fun example use case is enabled when NFT created on one network nests inside an NFT created in another network
  • UniqueInstancesAdapter for reserve-based NFT transfers nicely abstracts NFT types

All good changes overall!

@xlc
Copy link
Contributor

xlc commented Jun 17, 2025

LGTM

Copy link
Contributor

@franciscoaguirre franciscoaguirre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a first iteration, didn't review the whole PR. Will continue reviewing later.

@@ -0,0 +1,66 @@
[package]
name = "pallet-derivatives"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
name = "pallet-derivatives"
name = "pallet-xcm-derivatives"

Even though it's in the xcm folder, the crate name has to include it.

Copy link
Contributor

@bkontur bkontur Jun 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, agree pallet-derivatives is not a good name, pallet-xcm-derivatives, what about pallet-asset-derivatives or pallet-xcm-asset-derivatives.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though it's in the xcm folder

Why does it even need to be under the xcm folder? Is it really part of the xcm module?

From what I see, the pallet itself only uses a few imports from xcm-builder:

use xcm_builder::unique_instances::{
	derivatives::{DerivativesRegistry, IterDerivativesRegistry},
	DerivativesExtra,
};

For example, the DerivativesRegistry trait and most of the code in xcm-builder/src/unique_instances/derivatives.rs don’t actually use any xcm types. This could easily be moved out of the XCM modules — maybe into frame-support/traits or somewhere else.

So at first glance, I’d say this pallet is entirely XCM-agnostic.

As a comparison, pallet-revive and pallet-contracts also use xcm-builder, and they live under substrate/frame.
There are quite a lot of new traits and structs introduced in this PR — I’d prefer to do some triage/clean-up and move things around a bit.

@@ -0,0 +1 @@
# derivatives
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add some README detailing the purpose of this and how to use it.

Comment on lines 39 to 40
//! Fungible derivatives require a privileged origin to be registered since they could be used as
//! fee payment assets. Conversely, Derivative NFT collections contain unique derivative objects
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is, not true? While they could be used for fee payment, they can only be used like that if they're present in a liquidity pool with DOT in the case of AH. Other chains could configure it differently and might even need them to be registered from a privileged origin, but it's not at all mandatory.

//!
//! Fungible derivatives require a privileged origin to be registered since they could be used as
//! fee payment assets. Conversely, Derivative NFT collections contain unique derivative objects
//! that don't affect the chain's fee system. They don't represent a payment asset but rather some
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They could actually be used for fee payment. A chain might create a system with NFT coupons for free execution.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, it could be. Though there is no real-world example at the moment, the XCM Payment API doesn't support that. Additionally, I believe there will be issues with weight refunds.
But yeah, still possible. I need to reformulate this.

Comment on lines 46 to 47
//! Requiring a privileged origin in this case is raising an unreasonable barrier for NFT
//! interoperability between chains.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you want anyone to create a derivative NFT representing any real remote NFT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving NFT assets across chains securely via XCM is a very innovative capability (bridges aside), and use cases for it are basically unknown.
In such a case, the loose permissions should encourage the development of use cases, which can then drive the limitations that should be put on rights to create the derivative collection. The capabilities to accomplish that technically will grow over time, and it will become easier to implement the fine-tuned rights management if the need arises. At this stage, the most important thing is to get use cases in the first place. Therefore, it seems there is no need to create extra limitations at this moment.

In the future, we could add the following safeguards:

  1. create_derivative will only start the registration process, sending the ReportMetadata XCM instruction to the corresponding chain and registering a handler-extrinsic that will finalize the registration.
  2. The handler-extrinsic can check the specific metadata fields and decide if it is okay to register such a derivative collection.
  3. Also, the reserve chain could decide not to report the metadata, effectively forbidding the creation of a derivative collection (e.g., such a chain could provide a way for a collection's owner to forbid metadata reporting).

This way, the registration process will be very simple without the need to involve any voting process, yet it will be secure. But given that XCM Asset Metadata isn't here yet, I'd suggest starting to experiment with a simple unprivileged registration.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrshiposha I see that you’ve put quite a bit of work into this — it’s really cool, and I like it. I’d definitely like to support you on this. To move forward, let’s start by moving pallet-derivatives out of the XCM subfolder - for example, to polkadot-sdk/substrate/frame/derivatives, or if it’s specifically about NFTs, then to polkadot-sdk/substrate/frame/nfts/derivatives:

  • It currently doesn’t provide any XCM functionality (yet). If I understand correctly, it’s more of a FRAME-level pallet used to store some mappings, without any cross-chain or consensus-related functionality.
  • Keeping it under the XCM folder could lead to the false assumption that the XCM team maintains or supports it.
  • As for the Uniques AssetTransactor integration supporting XCM assets/instructions — that can stay in xcm-builder.

I’d suggest we start there 👆 , and then revisit the other traits/stuff added.


In the future, we could add the following safeguards:
create_derivative will only start the registration process, sending the ReportMetadata XCM instruction to the corresponding..

Exactly. Then we can add a type SendXcm to the pallet-derivatives, or introduce some other trait that will craft an XCM program. Again, this aligns with my earlier comments that pallet-derivatives doesn’t belong under the XCM modules. I still see pallet-derivatives as a kind of utility pallet to support NFT-related functionality, not as an XCM module.

Do we want to handle NFT transfers via XCM? That would be great! What are we missing to make that happen? Do we need new XCM instruction(s)? If so, why not start by defining them - perhaps through an XCM RFC? We should begin with a clear narrative and use cases, and/or propose an RFC for any new instructions that would be needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’d suggest we start there 👆 , and then revisit the other traits/stuff added.

Will do!

Do we need new XCM instruction(s)?

The current instruction set is enough for transferring NFTs. At least via reserve-based transfers, since they just transfer the "ownership" without relocating the original object, while teleports require the metadata transfer. The data transfer is addressed by the XCM Asset Metadata RFC: https://polkadot-fellows.github.io/RFCs/approved/0125-xcm-asset-metadata.html.

The only thing we need are clear and general tools for making AssetTransactors for the existing NFT solutions.

It's just that NFT implementations are diverse because there could be a lot of aspects to the unique objects: different ID assignment approaches and metadata management, such as whether to destroy the metadata on destroying an NFT or not. The ecosystem already has different implementations: pallet-nfts, pallet-uniques, Unique Network, ORML NFTs, Aventus NFTs. Each of them follows their own path. There are similarities as well, of course, but generally they don't fall into "one big NFT interface".

We should begin with a clear narrative and use cases, and/or propose an RFC for any new instructions that would be needed.

Hmm. Maybe I should revisit my Sub0 presentations (note that at the time this was an early concept) Lisbon 2023, Bangkok 2024 and make a corresponding text that will include the "big picture," including the future XCM Asset Metadata instruction uses.

Comment on lines 357 to 360
/// The `NoStoredMapping` adapter calls the `CreateOp` (which should take the `Original` value and
/// return a `Derivative` one) and returns `None`, indicating that the mapping between the original
/// and the derivative shouldn't be saved.
pub struct NoStoredMapping<CreateOp>(PhantomData<CreateOp>);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why might you want to not store the mapping? Because the ids are the same? Can you add that to this doc comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, when IDs are the same, we don't need a mapping. In this scenario, the pallet only provides an API for derivative creation/destruction.

I'll highlight this in the doc comment

@@ -158,6 +160,26 @@ impl<
}
}

pub struct MatchInClassInstances<Matcher>(PhantomData<Matcher>);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add some docs here for pub stuff

@@ -0,0 +1,66 @@
[package]
name = "pallet-derivatives"
Copy link
Contributor

@bkontur bkontur Jun 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, agree pallet-derivatives is not a good name, pallet-xcm-derivatives, what about pallet-asset-derivatives or pallet-xcm-asset-derivatives.

@@ -0,0 +1,66 @@
[package]
name = "pallet-derivatives"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though it's in the xcm folder

Why does it even need to be under the xcm folder? Is it really part of the xcm module?

From what I see, the pallet itself only uses a few imports from xcm-builder:

use xcm_builder::unique_instances::{
	derivatives::{DerivativesRegistry, IterDerivativesRegistry},
	DerivativesExtra,
};

For example, the DerivativesRegistry trait and most of the code in xcm-builder/src/unique_instances/derivatives.rs don’t actually use any xcm types. This could easily be moved out of the XCM modules — maybe into frame-support/traits or somewhere else.

So at first glance, I’d say this pallet is entirely XCM-agnostic.

As a comparison, pallet-revive and pallet-contracts also use xcm-builder, and they live under substrate/frame.
There are quite a lot of new traits and structs introduced in this PR — I’d prefer to do some triage/clean-up and move things around a bit.

mrshiposha and others added 2 commits June 19, 2025 15:23
Co-authored-by: Branislav Kontur <bkontur@gmail.com>
Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
Comment on lines 28 to 34
fn try_register_derivative(original: &Original, derivative: &Derivative) -> DispatchResult;

fn try_deregister_derivative_of(original: &Original) -> DispatchResult;

fn get_derivative(original: &Original) -> Result<Derivative, DispatchError>;

fn get_original(derivative: &Derivative) -> Result<Original, DispatchError>;
Copy link
Contributor

@bkontur bkontur Jun 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First of all, I don't like exposing DispatchResult and DispatchError in the XCM traits here. If you look at the DispatchError variants, which one would you use for try_register_derivative? In most cases, you'll likely end up probably using DispatchError::Other. XCM modules should be as independent of FRAME-specific types as possible. (I know we use some - for example, xcm-builder only uses DispatchResult for testing salary - which I also think should be removed and replaced also).

XCM processing isn't about dispatchables; it's about interpreting and executing XCM programs and instructions. So if this logic is truly XCM-related, you should use something like Result<T, XcmError> instead.

In fact, the DerivativesRegistry and related logic seem more FRAME-oriented and could probably be moved out of the XCM module altogether, along with the pallet derivatives, as I mentioned earlier.

/// This is the legacy return type of `Dispatchable`. It is still exposed for compatibility reasons.
/// The new return type is `DispatchResultWithInfo`. FRAME runtimes should use
/// `frame_support::dispatch::DispatchResult`.
pub type DispatchResult = core::result::Result<(), DispatchError>;

/// Reason why a dispatch call failed.
pub enum DispatchError {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, nothing from the file polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs is used by either polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs or polkadot/xcm/xcm-builder/src/unique_instances/ops.rs. So I’d say the entire derivatives.rs file can be cleanly moved into pallet-derivatives.

Once pallet-derivatives is moved to substrate/frame (as previously suggested), you can also properly take advantage of using DispatchResult and DispatchError. I also think that polkadot/xcm/xcm-builder/src/unique_instances/ops.rs could be moved directly into pallet-derivatives as well.

I would really recommend splitting this PR into the parts:

Comment on lines 48 to 57
/// The `DerivativeToOriginalConvert` uses the provided [DerivativesRegistry] to
/// convert the `Derivative` value to the `Original` one.
pub struct DerivativeToOriginalConvert<R>(PhantomData<R>);
impl<Original, Derivative, R: DerivativesRegistry<Original, Derivative>>
FallibleConvert<Derivative, Original> for DerivativeToOriginalConvert<R>
{
fn fallible_convert(a: Derivative) -> Result<Original, DispatchError> {
R::get_original(&a)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all this stuff around FallibleConvert is not really necessary, and everything what is needed can be done with existing tools e.g. TryConvert / TryConvertBack.

For example, if you use TryConvertBack, then we don't need two OriginalToDerivativeConvert and DerivativeToOriginalConvert - you can have just one OriginalToDerivativeConvert which implements both TryConvert and TryConvertBack - this way + removing FallibleConvert - lots of code can go away, which just simplifies the stuff.

Suggested change
/// The `DerivativeToOriginalConvert` uses the provided [DerivativesRegistry] to
/// convert the `Derivative` value to the `Original` one.
pub struct DerivativeToOriginalConvert<R>(PhantomData<R>);
impl<Original, Derivative, R: DerivativesRegistry<Original, Derivative>>
FallibleConvert<Derivative, Original> for DerivativeToOriginalConvert<R>
{
fn fallible_convert(a: Derivative) -> Result<Original, DispatchError> {
R::get_original(&a)
}
}

For FallibleConvert, if the conversion fails, which variant are you planning to use?

pub enum DispatchError {
	/// Some error occurred.
	Other(
		#[codec(skip)]
		#[cfg_attr(feature = "serde", serde(skip_deserializing))]
		&'static str,
	),
	/// Failed to lookup some data.
	CannotLookup,
	/// A bad origin.
	BadOrigin,
	/// A custom error in a module.
	Module(ModuleError),
	/// At least one consumer is remaining so the account cannot be destroyed.
	ConsumerRemaining,
	/// There are no providers so the account cannot be created.
	NoProviders,
	/// There are too many consumers so the account cannot be created.
	TooManyConsumers,
	/// An error to do with tokens.
	Token(TokenError),
	/// An arithmetic error.
	Arithmetic(ArithmeticError),
	/// The number of transactional layers has been reached, or we are not in a transactional
	/// layer.
	Transactional(TransactionalError),
	/// Resources exhausted, e.g. attempt to read/write data which is too large to manipulate.
	Exhausted,
	/// The state is corrupt; this is generally not going to fix itself.
	Corruption,
	/// Some resource (e.g. a preimage) is unavailable right now. This might fix itself later.
	Unavailable,
	/// Root origin is not allowed.
	RootNotAllowed,
	/// An error with tries.
	Trie(TrieError),
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For FallibleConvert, if the conversion fails, which variant are you planning to use?

The Module variant. The FallibleConvert is supposed to propagate pallet errors.

For instance, as it is used in the DeriveStrategyThenCreate type.
The NFT traits (Create, Update, Destroy, etc.) are supposed to usually be called from an extrinsic context, so they return a DispatchError when something goes wrong (containing a Module error since it will always be some pallet that implements the corresponding NFT operation).
The FallibleConvert's purpose is to return a proper pallet error, so we could return it inside an NFT operation.

For example, in the pallet tests, the CreateDerivativeOwnedBySovAcc instance will return the pallet_derivatives::Error::InvalidAsset error if there is no way to get a sovereign account for the given asset ID, and the error will be propagated through the create_derivative extrinsic back to the client.
Also, if an error occurs inside the NFT engine, it will be propagated as well.

Another util type where FallibleConvert is used is MapId. FallibleConvert helps to avoid using DispatchError::Other inside MapId's NFT traits implementations.
In the pallet tests, the OriginalToDerivativeConvert is used where FallibleConvert is needed.
(However, I acknowledge that in the XCM context, it is of little use since if an error occurs, it will be converted to FailedToTransactAsset anyway. But it could be useful if this were called from an extrinsic, not from an XCM context, as in the case above).

I'm all for improving the error reporting and don't mind refactoring this. I just want to be able to propagate the pallet errors in NFT operations so that extrinsics where they are called could report the proper errors (especially when operations are nested under other pallets' APIs, such as pallet-nft-fractionalization).

@Polkadot-Forum
Copy link

This pull request has been mentioned on Polkadot Forum. There might be relevant details there:

https://forum.polkadot.network/t/2025-06-20-technical-fellowship-opendev-call/13443/1

Comment on lines 20 to 21
PredefinedIdDerivativeCollections::create_derivative(RuntimeOrigin::signed(1), id.clone())
.unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
PredefinedIdDerivativeCollections::create_derivative(RuntimeOrigin::signed(1), id.clone())
.unwrap();
assert_ok!(PredefinedIdDerivativeCollections::create_derivative(RuntimeOrigin::signed(1), id.clone()));

Comment on lines 48 to 49
PredefinedIdDerivativeCollections::destroy_derivative(RuntimeOrigin::root(), id.clone())
.unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
PredefinedIdDerivativeCollections::destroy_derivative(RuntimeOrigin::root(), id.clone())
.unwrap();
assert_ok!(PredefinedIdDerivativeCollections::destroy_derivative(RuntimeOrigin::root(), id.clone()));

DispatchError::BadOrigin,
);

AutoIdDerivativeCollections::create_derivative(RuntimeOrigin::signed(3), id_a.clone())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrshiposha please use better asset_ok instead of unwrap where possible

@@ -0,0 +1,150 @@
use core::marker::PhantomData;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrshiposha please also check all other files, they are missing headers (licenses and stuff)

@@ -587,6 +587,21 @@ impl<A, B: Default> Convert<A, B> for () {
}
}

/// Fallible conversion returning either the converted value or the
/// [DispatchError](crate::DispatchError) on failure.
pub trait FallibleConvert<A, B> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually, from what I’ve seen, a lot of conversions — especially in the XCM module - simply return the original A value as an error when they fail. So in such cases, you don’t really need a FallibleConvert.

If we wanted to go deeper, we could introduce something more generic (and arguably over-complicated), like:

pub trait FallibleConvert<A, B, E> {
	/// Attempt to perform the conversion.
	fn fallible_convert(a: A) -> Result<B, E>;
}

But then, when you have something like SomeImplFallibleConvert<A, B, DispatchResult> and/or OtherImplFallibleConvert<A, B, XcmError>, you’d need to deal with error conversions and additional boilerplate. Let’s not overthink it unless it’s truly necessary.

That said, I’d definitely start with the cleanup here:
https://github.com/paritytech/polkadot-sdk/pull/4300/files#r2160173529

And after that, maybe we will see that this FallibleConvert can also go in the pallet-derivatives.

* move pallet-derivatives from polkadot/xcm to substrate/frame
* move contents of the `<XCM_BUILDER>/unique_instances/derivatives.rs` to `<FRAME_PALLET_DERIVATIVES>/misc.rs`
* move `OwnerConvertedLocation` from `<XCM_BUILDER>/unique_instances/ops.rs` to `<FRAME_PALLET_DERIVATIVES>/misc.rs`
* all uses of `FallibleConvert` are limitied by the pallet-derivatives and asset-ops utils (e.g., `MapId`)
* add missing licenses
* add docs for new public entities in xcm-builder
* revisit motivation for pallet-derivatives
* add README text for pallet-derivatives
* remove `unwrap` from the pallet tests where possible
@mrshiposha
Copy link
Contributor Author

@bkontur @franciscoaguirre please take a look at this commit: cd4df12.

It should address the current review comments.

NOTE: <XCM_BUILDER>/unique_instances/ops.rs was not moved to the pallet-derivatives. Only the OwnerConvertedLocation was moved there. Other types can be used for AssetTransactors that deal with both originals and derivatives. So, it seems they are more XCM-specific, so I left them there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T6-XCM This PR/Issue is related to XCM.
Projects
Status: In-Review
Development

Successfully merging this pull request may close these issues.

10 participants